package org.nlp2rdf.navigator.component.learn;

import java.util.Set;

import org.apache.log4j.Logger;
import org.dllearner.algorithms.ocel.OCEL;
import org.dllearner.core.ComponentInitException;
import org.dllearner.core.ComponentManager;
import org.dllearner.core.EvaluatedDescription;
import org.dllearner.core.KnowledgeSource;
import org.dllearner.core.LearningAlgorithm;
import org.dllearner.core.LearningProblem;
import org.dllearner.core.LearningProblemUnsupportedException;
import org.dllearner.core.ReasonerComponent;
import org.dllearner.core.configurators.ComponentFactory;
import org.dllearner.core.configurators.ROLComponent2Configurator;
import org.dllearner.learningproblems.PosNegLPStandard;
import org.dllearner.reasoning.FastInstanceChecker;
import org.dllearner.utilities.experiments.Examples;
import org.nlp2rdf.navigator.client.common.ConceptDTO;
import org.nlp2rdf.navigator.client.common.LearnedConceptDTO;
import org.nlp2rdf.navigator.client.common.LearnedConceptDTO.MyExamples;
import org.nlp2rdf.navigator.client.exception.NKELearningException;
import org.nlp2rdf.navigator.component.Component;

public class Learn extends Component implements ILearn {
	private static final Logger logger = Logger.getLogger(Learn.class);

	private static boolean WRITE_SEARCHTREE = false;

	private IKnowledgeSources knowledgeSource;

	private int maxExecutionTime = 30;
	private int minExecutionTime = 10;
	private int noisePercentage = 0;
	private boolean useHasValueConstructor = false;
	private boolean useDataHasValueConstructor = true;
	private String startClass = null;
	private Set<String> ignoredConcepts = null;
	private LearningAlgorithm la;

	@Override
	public String toString() {
		return maxExecutionTime + " " + noisePercentage;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.nlp2rdf.navigator.component.Component#_init()
	 */
	@Override
	public void _init() {
		// do nothing
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.nlp2rdf.navigator.component.learn.ILearn#learn(org.dllearner.utilities
	 * .experiments.Examples)
	 */
	public LearnedConceptDTO learn(Examples ex) throws NKELearningException {
		check();

        EvaluatedDescription ed = learnDescription(ex);
        // logger.info("getting description");
        // logger.info(la.getCurrentlyBestMostGeneralEvaluatedDescriptions());
        ConceptDTO c = new ConceptDTO(ed.getDescription().toKBSyntaxString(), ed.getDescription()
                .toManchesterSyntaxString(null, null));
        MyExamples exDTO = new MyExamples();
        LearnedConceptDTO result = new LearnedConceptDTO(c, ed.getAccuracy(), exDTO);

        logger.debug(result.toString());
        for (EvaluatedDescription edlog : la.getCurrentlyBestEvaluatedDescriptions(10)) {
            logger.debug(edlog.getDescription().toKBSyntaxString());
            logger.debug(edlog.getDescription().toManchesterSyntaxString(null, null));
        }

        return result;
	}


	private void init(Set<KnowledgeSource> knowledgeSources, FastInstanceChecker rc, LearningProblem lp,
			LearningAlgorithm la) throws NKELearningException {
		try {
			for (KnowledgeSource ks : knowledgeSources) {
				ks.init();
			}

			rc.init();
			lp.init();
			la.init();
		} catch (ComponentInitException e) {
			String msg = "A problem during initialisation of DL-Learner occured\n" + e.getMessage();
			logger.error(msg);
			throw new NKELearningException(msg);
		}

	}

	private LearningAlgorithm _getROLLearner(LearningProblem lp, ReasonerComponent rc, Examples ex) {

		OCEL la = null;
		try {
			la = ComponentFactory.getROLComponent2(lp, rc);
		} catch (LearningProblemUnsupportedException e) {
			logger.error("Learn: this should never happen." + e.getMessage());

		}
		/*
		 * STATIC
		 */
		ROLComponent2Configurator conf = la.getConfigurator();
		conf.setUseExistsConstructor(true);
		conf.setUseAllConstructor(false);
		conf.setUseCardinalityRestrictions(false);
		conf.setUseNegation(false);

		conf.setUseBooleanDatatypes(true);
		conf.setTerminateOnNoiseReached(true);
		/*
		 * DYNAMIC
		 */

		// if there are many examples allow an error or two
		// 0.10 = allow one miss for 10 positive examples
		int allowance = Math.max(1, (int) Math.floor(ex.getPosTrain().size() * 0.10));
		conf.setValueFrequencyThreshold(ex.getPosTrain().size() - allowance);

		conf.setMinExecutionTimeInSeconds(minExecutionTime);
		conf.setNoisePercentage(noisePercentage);
		conf.setUseHasValueConstructor(useHasValueConstructor);
		conf.setUseDataHasValueConstructor(useDataHasValueConstructor);

		conf.setMaxExecutionTimeInSeconds(maxExecutionTime);
		if (startClass != null) {
			conf.setStartClass(startClass);
		}
		if (ignoredConcepts != null) {
			conf.setIgnoredConcepts(ignoredConcepts);
		}
		if (WRITE_SEARCHTREE) {
			conf.setWriteSearchTree(true);
			conf.setSearchTreeFile("searchTree.txt");
			conf.setReplaceSearchTree(true);
		}

		return la;

	}

	public void setMaxExecutionTime(int maxExecutionTime) {
		this.maxExecutionTime = maxExecutionTime;
	}

    public int getMaxExecutionTime() {
        return maxExecutionTime;
    }

    public void setNoisePercentage(int noisePercentage) {
		this.noisePercentage = noisePercentage;
	}

	public void setUseHasValueConstructor(boolean useHasValueConstructor) {
		this.useHasValueConstructor = useHasValueConstructor;
	}

	public void setUseDataHasValueConstructor(boolean useDataHasValueConstructor) {
		this.useDataHasValueConstructor = useDataHasValueConstructor;
	}

	public void setStartClass(String startClass) {
		this.startClass = startClass;
	}

	public void setIgnoredConcepts(Set<String> ignoredConcepts) {
		this.ignoredConcepts = ignoredConcepts;
	}

	public LearningAlgorithm getLearningAlgorithm() {
		return la;
	}

    public EvaluatedDescription learnDescription(Examples ex) throws NKELearningException {
		check();

		// initialize the objects
		Set<KnowledgeSource> knowledgeSources = knowledgeSource.getKnowledgeSources(ex);

		FastInstanceChecker rc = ComponentFactory.getFastInstanceChecker(knowledgeSources);
		PosNegLPStandard lp = ComponentFactory.getPosNegLPStandard(rc, ex.getPosTrain(), ex.getNegTrain());

		// TODO test
		// lp.getConfigurator().setUseApproximations(true);
		// lp.getConfigurator().setAccuracyMethod("fmeasure");

		// LearningAlgorithm
		la = _getROLLearner(lp, rc, ex);

		init(knowledgeSources, rc, lp, la);
		la.start();
		try {
			EvaluatedDescription ed = la.getCurrentlyBestEvaluatedDescription();
			return ed;
		} finally {
			for (KnowledgeSource ks : knowledgeSources) {
				ComponentManager.getInstance().freeComponent(ks);
			}
			ComponentManager.getInstance().freeComponent(rc);
			ComponentManager.getInstance().freeComponent(lp);
		    ComponentManager.getInstance().freeComponent(la);
		}
    }

    public void setKnowledgeSource(IKnowledgeSources knowledgeSource) {
		this.knowledgeSource = knowledgeSource;
	}

}
